home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / AbstractDocument.java < prev    next >
Text File  |  1998-06-30  |  62KB  |  2,065 lines

  1. /*
  2.  * @(#)AbstractDocument.java    1.77 98/04/09
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20. package com.sun.java.swing.text;
  21.  
  22. import java.util.*;
  23. import java.io.*;
  24.  
  25. import com.sun.java.swing.undo.*;
  26. import com.sun.java.swing.event.ChangeListener;
  27. import com.sun.java.swing.event.*;
  28.  
  29. /**
  30.  * An implementation of the document interface to serve as a 
  31.  * basis for implementing various kinds of documents.  At this
  32.  * level there is very little policy, so there is a corresponding
  33.  * increase in difficulty of use.
  34.  * <p>
  35.  * This class implements a locking mechanism for the document.  It
  36.  * allows multiple readers or one writer, and writers must wait until 
  37.  * all observers of the document have been notified of a previous 
  38.  * change before beginning another mutation to the document.  The
  39.  * read lock is aquired and released using the <code>render</code>
  40.  * method.  A write lock is aquired by the methods that mutate the
  41.  * document, and are held for the duration of the method call.
  42.  * Notification is done on the thread that produced the mutation, 
  43.  * and the thread has full read access to the document for the
  44.  * duration of the notification, but other readers are kept out
  45.  * until the notification has finished.  The notification is a
  46.  * beans event notification which does not allow any further 
  47.  * mutations until all listeners have been notified.
  48.  * <p>
  49.  * Any models subclassed from this class and used in conjunction
  50.  * with a text component that has a look and feel implementation
  51.  * that is derived from DefaultTextUI may be safely updated
  52.  * asynchronously.  The rendering in DefaultTextUI occurs under
  53.  * the protection of the <code>render</code> method, which makes
  54.  * rendering safe if it is running on the event thread (which
  55.  * happens if using repaint to schedule updates).  The code path
  56.  * for any DocumentListener implementation must not access the
  57.  * component lock if trying to be safe from deadlocks.  
  58.  * The <cod>repaint</code> and <code>revalidate</code> methods 
  59.  * on JComponent are safe.
  60.  * <p>
  61.  * Warning: serialized objects of this class will not be compatible with
  62.  * future swing releases.  The current serialization support is appropriate 
  63.  * for short term storage or RMI between Swing1.0 applications.  It will
  64.  * not be possible to load serialized Swing1.0 objects with future releases
  65.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  66.  * baseline for the serialized form of Swing objects.
  67.  *
  68.  * @author  Timothy Prinzing
  69.  * @version 1.77 04/09/98
  70.  */
  71. public abstract class AbstractDocument implements Document, Serializable {
  72.  
  73.     /**
  74.      * Constructs a new AbstractDocument, wrapped around some
  75.      * specified content storage mechanism.
  76.      *
  77.      * @param data the content
  78.      */
  79.     protected AbstractDocument(Content data) {
  80.     this(data, StyleContext.getDefaultStyleContext());
  81.     }
  82.  
  83.     /**
  84.      * Constructs a new AbstractDocument, wrapped around some
  85.      * specified content storage mechanism.
  86.      *
  87.      * @param data the content
  88.      * @param context the attribute context
  89.      */
  90.     protected AbstractDocument(Content data, AttributeContext context) {
  91.     this.data = data;
  92.     this.context = context;
  93.     }
  94.  
  95.     /**
  96.      * Support for managing a set of properties. Callers
  97.      * can use the documentProperties dictionary to annotate the
  98.      * document with document-wide properties.
  99.      * 
  100.      * @return a non null Dictionary 
  101.      * @see #setDocumentProperties
  102.      */
  103.     public Dictionary getDocumentProperties() {
  104.     if (documentProperties == null) {
  105.         documentProperties = new Hashtable(2);
  106.     }
  107.     return documentProperties;
  108.     }
  109.  
  110.     /**
  111.      * Replace the document properties dictionary for this document.
  112.      * 
  113.      * @param x the new dictionary
  114.      * @see #getDocumentProperties
  115.      */
  116.     public void setDocumentProperties(Dictionary x) {
  117.     documentProperties = x;
  118.     }
  119.  
  120.     /**
  121.      * Notifies all listeners that have registered interest for
  122.      * notification on this event type.  The event instance 
  123.      * is lazily created using the parameters passed into 
  124.      * the fire method.
  125.      *
  126.      * @param e the event
  127.      * @see EventListenerList
  128.      */
  129.     protected void fireInsertUpdate(DocumentEvent e) {
  130.     // Guaranteed to return a non-null array
  131.     Object[] listeners = listenerList.getListenerList();
  132.     // Process the listeners last to first, notifying
  133.     // those that are interested in this event
  134.     for (int i = listeners.length-2; i>=0; i-=2) {
  135.         if (listeners[i]==DocumentListener.class) {
  136.         // Lazily create the event:
  137.         // if (e == null)
  138.         // e = new ListSelectionEvent(this, firstIndex, lastIndex);
  139.         ((DocumentListener)listeners[i+1]).insertUpdate(e);
  140.         }           
  141.     }
  142.     }
  143.  
  144.     /**
  145.      * Notifies all listeners that have registered interest for
  146.      * notification on this event type.  The event instance 
  147.      * is lazily created using the parameters passed into 
  148.      * the fire method.
  149.      *
  150.      * @param e the event
  151.      * @see EventListenerList
  152.      */
  153.     protected void fireChangedUpdate(DocumentEvent e) {
  154.     // Guaranteed to return a non-null array
  155.     Object[] listeners = listenerList.getListenerList();
  156.     // Process the listeners last to first, notifying
  157.     // those that are interested in this event
  158.     for (int i = listeners.length-2; i>=0; i-=2) {
  159.         if (listeners[i]==DocumentListener.class) {
  160.         // Lazily create the event:
  161.         // if (e == null)
  162.         // e = new ListSelectionEvent(this, firstIndex, lastIndex);
  163.         ((DocumentListener)listeners[i+1]).changedUpdate(e);
  164.         }           
  165.     }
  166.     }
  167.  
  168.     /**
  169.      * Notifies all listeners that have registered interest for
  170.      * notification on this event type.  The event instance 
  171.      * is lazily created using the parameters passed into 
  172.      * the fire method.
  173.      *
  174.      * @param e the event
  175.      * @see EventListenerList
  176.      */
  177.     protected void fireRemoveUpdate(DocumentEvent e) {
  178.     // Guaranteed to return a non-null array
  179.     Object[] listeners = listenerList.getListenerList();
  180.     // Process the listeners last to first, notifying
  181.     // those that are interested in this event
  182.     for (int i = listeners.length-2; i>=0; i-=2) {
  183.         if (listeners[i]==DocumentListener.class) {
  184.         // Lazily create the event:
  185.         // if (e == null)
  186.         // e = new ListSelectionEvent(this, firstIndex, lastIndex);
  187.         ((DocumentListener)listeners[i+1]).removeUpdate(e);
  188.         }           
  189.     }
  190.     }
  191.  
  192.     /**
  193.      * Notifies all listeners that have registered interest for
  194.      * notification on this event type.  The event instance 
  195.      * is lazily created using the parameters passed into 
  196.      * the fire method.
  197.      *
  198.      * @param e the event
  199.      * @see EventListenerList
  200.      */
  201.     protected void fireUndoableEditUpdate(UndoableEditEvent e) {
  202.     // Guaranteed to return a non-null array
  203.     Object[] listeners = listenerList.getListenerList();
  204.     // Process the listeners last to first, notifying
  205.     // those that are interested in this event
  206.     for (int i = listeners.length-2; i>=0; i-=2) {
  207.         if (listeners[i]==UndoableEditListener.class) {
  208.         // Lazily create the event:
  209.         // if (e == null)
  210.         // e = new ListSelectionEvent(this, firstIndex, lastIndex);
  211.         ((UndoableEditListener)listeners[i+1]).undoableEditHappened(e);
  212.         }           
  213.     }
  214.     }
  215.  
  216.     // --- Document methods -----------------------------------------
  217.  
  218.     /**
  219.      * This allows the model to be safely rendered in the presence
  220.      * of currency, if the model supports being updated asynchronously.
  221.      * The given runnable will be executed in a way that allows it
  222.      * to safely read the model with no changes while the runnable
  223.      * is being executed.  The runnable itself may <em>not</em>
  224.      * make any mutations. 
  225.      * <p>
  226.      * This is implemented to aquire a read lock for the duration
  227.      * of the runnables execution.  There may be multiple runnables
  228.      * executing at the same time, and all writers will be blocked
  229.      * while there are active rendering runnables.  If the runnable
  230.      * throws an exception, its lock will be safely released.
  231.      * There is no protection against a runnable that never exits,
  232.      * which will effectively leave the document locked for it's
  233.      * lifetime.
  234.      * <p>
  235.      * If the given runnable attempts to make any mutations in
  236.      * this implementation, a deadlock will occur.  There is
  237.      * no tracking of individual rendering threads to enable
  238.      * detecting this situation, but a subclass could incur
  239.      * the overhead of tracking them and throwing an error.
  240.      * <p>
  241.      * This method is thread safe, although most Swing methods
  242.      * are not. Please see 
  243.      * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  244.      * and Swing</A> for more information.
  245.      *
  246.      * @param r the renderer to execute.
  247.      */
  248.     public void render(Runnable r) {
  249.     try {
  250.         readLock();
  251.         r.run();
  252.     } finally {
  253.         readUnlock();
  254.     }
  255.     }
  256.  
  257.     /**
  258.      * Returns the length of the data.  This is the number of
  259.      * characters of content that represents the users data.
  260.      *
  261.      * @return the length >= 0
  262.      * @see Document#getLength
  263.      */
  264.     public int getLength() {
  265.     return data.length() - 1;
  266.     }
  267.  
  268.     /**
  269.      * Adds a document listener for notification of any changes.
  270.      *
  271.      * @param listener the listener
  272.      * @see Document#addDocumentListener
  273.      */
  274.     public void addDocumentListener(DocumentListener listener) {
  275.     listenerList.add(DocumentListener.class, listener);
  276.     }
  277.  
  278.     /**
  279.      * Removes a document listener.
  280.      *
  281.      * @param listener the listener
  282.      * @see Document#removeDocumentListener
  283.      */
  284.     public void removeDocumentListener(DocumentListener listener) {
  285.     listenerList.remove(DocumentListener.class, listener);
  286.     }
  287.  
  288.     /**
  289.      * Adds an undo listener for notification of any changes.
  290.      * Undo/Redo operations performed on the UndoableEdit will
  291.      * cause the appropriate DocumentEvent to be fired to keep
  292.      * the view(s) in sync with the model.
  293.      *
  294.      * @param listener the listener
  295.      * @see Document#addUndoableEditListener
  296.      */
  297.     public void addUndoableEditListener(UndoableEditListener listener) {
  298.     listenerList.add(UndoableEditListener.class, listener);
  299.     }
  300.  
  301.     /**
  302.      * Removes an undo listener.
  303.      *
  304.      * @param listener the listener
  305.      * @see Document#removeDocumentListener
  306.      */
  307.     public void removeUndoableEditListener(UndoableEditListener listener) {
  308.     listenerList.remove(UndoableEditListener.class, listener);
  309.     }
  310.  
  311.     /**
  312.      * A convenience method for looking up a property value. It is
  313.      * equivalent to:
  314.      * <pre>
  315.      * getDocumentProperties().get(key);
  316.      * </pre>
  317.      * 
  318.      * @param key the non-null property key
  319.      * @return the value of this property or null
  320.      * @see #getDocumentProperties
  321.      */
  322.     public final Object getProperty(Object key) {
  323.         return getDocumentProperties().get(key);
  324.     }
  325.  
  326.  
  327.     /**
  328.      * A convenience method for storing up a property value.  It is
  329.      * equivalent to:
  330.      * <pre>
  331.      * getDocumentProperties().put(key, value);
  332.      * </pre>
  333.      * If value is null this method will remove the property
  334.      * 
  335.      * @param key the non-null key
  336.      * @param value the value
  337.      * @see #getDocumentProperties
  338.      */
  339.     public final void putProperty(Object key, Object value) {
  340.     if (value != null) {
  341.         getDocumentProperties().put(key, value);
  342.     } else
  343.             getDocumentProperties().remove(key);
  344.     }
  345.  
  346.     /**
  347.      * Removes some content from the document.
  348.      * Removing content causes a write lock to be held while the
  349.      * actual changes are taking place.  Observers are notified
  350.      * of the change on the thread that called this method.
  351.      * <p>
  352.      * This method is thread safe, although most Swing methods
  353.      * are not. Please see 
  354.      * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  355.      * and Swing</A> for more information.
  356.      * 
  357.      * @param offs the starting offset >= 0
  358.      * @param len the number of characters to remove >= 0
  359.      * @exception BadLocationException  the given remove position is not a valid 
  360.      *   position within the document
  361.      * @see Document#remove
  362.      */
  363.     public void remove(int offs, int len) throws BadLocationException {
  364.     if (len > 0) {
  365.         try {
  366.         writeLock();
  367.         DefaultDocumentEvent chng = 
  368.             new DefaultDocumentEvent(offs, len, DocumentEvent.EventType.REMOVE);
  369.         removeUpdate(chng);
  370.         UndoableEdit u = data.remove(offs, len);
  371.         if (u != null) {
  372.             chng.addEdit(u);
  373.         }
  374.         // Mark the edit as done.
  375.         chng.end();
  376.         fireRemoveUpdate(chng);
  377.         // only fire undo if Content implementation supports it
  378.         if (u != null) {
  379.             fireUndoableEditUpdate(new UndoableEditEvent(this, chng));
  380.         }
  381.         } finally {
  382.         writeUnlock();
  383.         }
  384.     }
  385.     }
  386.  
  387.     /**
  388.      * Inserts some content into the document.
  389.      * Inserting content causes a write lock to be held while the
  390.      * actual changes are taking place, followed by notification
  391.      * to the observers on the thread that grabbed the write lock.
  392.      * <p>
  393.      * This method is thread safe, although most Swing methods
  394.      * are not. Please see 
  395.      * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  396.      * and Swing</A> for more information.
  397.      *
  398.      * @param offs the starting offset >= 0
  399.      * @param str the string to insert; does nothing with null/empty strings
  400.      * @param a the attributes for the inserted content
  401.      * @exception BadLocationException  the given insert position is not a valid 
  402.      *   position within the document
  403.      * @see Document#insertString
  404.      */
  405.     public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
  406.     if ((str == null) || (str.length() == 0)) {
  407.         return;
  408.     }
  409.     try {
  410.         writeLock();
  411.         UndoableEdit u = data.insertString(offs, str);
  412.         DefaultDocumentEvent e = 
  413.         new DefaultDocumentEvent(offs, str.length(), DocumentEvent.EventType.INSERT);
  414.         if (u != null) {
  415.         e.addEdit(u);
  416.         }
  417.         insertUpdate(e, a);
  418.         // Mark the edit as done.
  419.         e.end();
  420.         fireInsertUpdate(e);
  421.         // only fire undo if Content implementation supports it
  422.         if (u != null) {
  423.         fireUndoableEditUpdate(new UndoableEditEvent(this, e));
  424.         }
  425.     } finally {
  426.         writeUnlock();
  427.     }
  428.     }
  429.  
  430.     /**
  431.      * Gets a sequence of text from the document.  
  432.      *
  433.      * @param offset the starting offset >= 0
  434.      * @param length the number of characters to retrieve >= 0
  435.      * @return the text
  436.      * @exception BadLocationException  the range given includes a position 
  437.      *   that is not a valid position within the document
  438.      * @see Document#getText
  439.      */
  440.     public String getText(int offset, int length) throws BadLocationException {
  441.     String str = data.getString(offset, length);
  442.     return str;
  443.     }
  444.  
  445.     /**
  446.      * Gets some text from the document potentially without
  447.      * making a copy.  The character array returned in the
  448.      * given <code>Segment</code> should never be mutated.
  449.      * This kind of access to the characters of the document
  450.      * is provided to help make the rendering potentially more
  451.      * efficient.  The caller should make no assumptions about
  452.      * the lifetime of the returned character array, which
  453.      * should be copied if needed beyond the use for rendering.
  454.      *
  455.      * @param offset the starting offset >= 0
  456.      * @param length the number of characters to retrieve >= 0
  457.      * @param txt the Segment object to retrieve the text into
  458.      * @exception BadLocationException  the range given includes a position 
  459.      *   that is not a valid position within the document
  460.      */
  461.     public void getText(int offset, int length, Segment txt) throws BadLocationException {
  462.     data.getChars(offset, length, txt);
  463.     }
  464.  
  465.     /**
  466.      * Returns a position that will track change as the document
  467.      * is altered.
  468.      * <p>
  469.      * This method is thread safe, although most Swing methods
  470.      * are not. Please see 
  471.      * <A HREF="http://java.sun.com/products/jfc/swingdoc-archive/threads.html">Threads
  472.      * and Swing</A> for more information.
  473.      *
  474.      * @param offs the position in the model >= 0
  475.      * @return the position
  476.      * @exception BadLocationException  if the given position does not
  477.      *   represent a valid location in the associated document
  478.      * @see Document#createPosition
  479.      */
  480.     public synchronized Position createPosition(int offs) throws BadLocationException {
  481.     return data.createPosition(offs);
  482.     }
  483.  
  484.     /**
  485.      * Returns a position that represents the start of the document.  The 
  486.      * position returned can be counted on to track change and stay 
  487.      * located at the beginning of the document.
  488.      *
  489.      * @return the position
  490.      */
  491.     public final Position getStartPosition() {
  492.     Position p;
  493.     try {
  494.         p = createPosition(0);
  495.     } catch (BadLocationException bl) {
  496.         p = null;
  497.     }
  498.     return p;
  499.     }
  500.     
  501.     /**
  502.      * Returns a position that represents the end of the document.  The
  503.      * position returned can be counted on to track change and stay 
  504.      * located at the end of the document.
  505.      *
  506.      * @return the position
  507.      */
  508.     public final Position getEndPosition() {
  509.     Position p;
  510.     try {
  511.         p = createPosition(data.length());
  512.     } catch (BadLocationException bl) {
  513.         p = null;
  514.     }
  515.     return p;
  516.     }
  517.  
  518.     /**
  519.      * Gets all root elements defined.  Typically, there
  520.      * will only be one so the default implementation
  521.      * is to return the default root element.
  522.      *
  523.      * @return the root element
  524.      */
  525.     public Element[] getRootElements() {
  526.     Element[] elems = new Element[1];
  527.     elems[0] = getDefaultRootElement();
  528.     return elems;
  529.     }
  530.  
  531.     /**
  532.      * Returns the root element that views should be based upon
  533.      * unless some other mechanism for assigning views to element
  534.      * structures is provided.
  535.      *
  536.      * @return the root element
  537.      * @see Document#getDefaultRootElement
  538.      */
  539.     public abstract Element getDefaultRootElement();
  540.  
  541.     // ---- local methods -----------------------------------------
  542.  
  543.     /**
  544.      * Fetches the context for managing attributes.  This
  545.      * method effectively establishes the strategy used 
  546.      * for compressing AttributeSet information.
  547.      *
  548.      * @return the context
  549.      */
  550.     protected final AttributeContext getAttributeContext() {
  551.     return context;
  552.     }
  553.  
  554.     /**
  555.      * Updates document structure as a result of text insertion.  This
  556.      * will happen within a write lock.  If a subclass of
  557.      * this class reimplements this method, it should delegate to the
  558.      * superclass as well.
  559.      *
  560.      * @param chng a description of the change
  561.      * @param attr the attributes for the change
  562.      */
  563.     protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr) {
  564.     }
  565.  
  566.     /**
  567.      * Updates any document structure as a result of text removal.
  568.      * This will happen within a write lock. If a subclass
  569.      * of this class reimplements this method, it should delegate to the
  570.      * superclass as well.
  571.      *
  572.      * @param chng a description of the change
  573.      */
  574.     protected void removeUpdate(DefaultDocumentEvent chng) {
  575.     }
  576.  
  577.     /**
  578.      * Gives a diagnostic dump.
  579.      *
  580.      * @param out the output stream
  581.      */
  582.     public void dump(PrintStream out) {
  583.     Element root = getDefaultRootElement();
  584.     if (root instanceof AbstractElement) {
  585.         ((AbstractElement)root).dump(out, 0);
  586.     }
  587.     }
  588.  
  589.     /**
  590.      * Gets the content for the document.
  591.      *
  592.      * @return the content
  593.      */
  594.     protected final Content getContent() {
  595.     return data;
  596.     }
  597.  
  598.     /**
  599.      * Creates a document leaf element.
  600.      * Hook through which elements are created to represent the 
  601.      * document structure.  Because this implementation keeps 
  602.      * structure and content seperate, elements grow automatically
  603.      * when content is extended so splits of existing elements 
  604.      * follow.  The document itself gets to decide how to generate 
  605.      * elements to give flexibility in the type of elements used.
  606.      *
  607.      * @param parent the parent element
  608.      * @param a the attributes for the element
  609.      * @param p0 the beginning of the range >= 0
  610.      * @param p1 the end of the range >= p0
  611.      * @return the new element
  612.      */
  613.     protected Element createLeafElement(Element parent, AttributeSet a, int p0, int p1) {
  614.     return new LeafElement(parent, a, p0, p1);
  615.     }
  616.  
  617.     /**
  618.      * Creates a document branch element, that can contain other elements.
  619.      *
  620.      * @param parent the parent element
  621.      * @param a the attributes
  622.      * @return the element
  623.      */
  624.     protected Element createBranchElement(Element parent, AttributeSet a) {
  625.     return new BranchElement(parent, a);
  626.     }
  627.  
  628.     // --- Document locking ----------------------------------
  629.  
  630.     /**
  631.      * Fetches the current writing thread if there is one.
  632.      * This can be used to distinguish whether a method is
  633.      * being called as part of an existing modification or
  634.      * if a lock needs to be acquired and a new transaction
  635.      * started.  
  636.      *
  637.      * @returns the thread actively modifying the document
  638.      *  or null if there are no modifications in progress
  639.      */
  640.     protected synchronized final Thread getCurrentWriter() {
  641.     return currWriter;
  642.     }
  643.  
  644.     /**
  645.      * Acquires a lock to begin mutating the document this lock
  646.      * protects.  There can be no notification of changes or
  647.      * reading going on in order to gain the lock.
  648.      */
  649.     protected synchronized final void writeLock() {
  650.     try {
  651.         while ((numReaders > 0) || (currWriter != null)) {
  652.         wait();
  653.         }
  654.         currWriter = Thread.currentThread();
  655.     } catch (InterruptedException e) {
  656.         // safe to let this pass... write lock not
  657.         // held if the thread lands here.
  658.     }
  659.     }
  660.  
  661.     /**
  662.      * Releases the write lock held because the write
  663.      * operation is finished.  This allows either a new
  664.      * writer or readers to aquire a lock.
  665.      */
  666.     protected synchronized final void writeUnlock() {
  667.     if ((numReaders > 0) || (currWriter == null)) {
  668.         throw new StateInvariantError(BAD_LOCK_STATE);
  669.     }
  670.     currWriter = null;
  671.     notify();
  672.     }
  673.  
  674.     /**
  675.      * Acquires a lock to begin reading some state from the 
  676.      * document.  There can be multiple readers at the same time
  677.      * and reading can occur while notification to the listeners 
  678.      * is going on, but writing blocks the readers.  
  679.      */
  680.     protected synchronized final void readLock() {
  681.     try {
  682.         while (currWriter != null) {
  683.         wait();
  684.         }
  685.         numReaders += 1;
  686.     } catch (InterruptedException e) {
  687.         // safe to let this pass... read lock not
  688.         // held if the thread lands here.
  689.     }
  690.     }
  691.  
  692.     /**
  693.      * Does a read unlock.
  694.      * One of the readers is done.  If there are no more readers
  695.      * then writing can begin again.
  696.      */
  697.     protected synchronized final void readUnlock() {
  698.     if (numReaders <= 0) {
  699.         throw new StateInvariantError(BAD_LOCK_STATE);
  700.     }
  701.     numReaders -= 1;
  702.     notify();
  703.     }
  704.  
  705.     // --- serialization ---------------------------------------------
  706.  
  707.     private void readObject(ObjectInputStream s)
  708.       throws ClassNotFoundException, IOException 
  709.     {
  710.     s.defaultReadObject();
  711.     listenerList = new EventListenerList();
  712.     }
  713.  
  714.     // ----- member variables ------------------------------------------
  715.  
  716.     private transient int numReaders;
  717.     private transient Thread currWriter;
  718.  
  719.     /**
  720.      * Storage for document-wide properties.
  721.      */
  722.     private Dictionary documentProperties = null;
  723.  
  724.     /**
  725.      * The event listener list for the document.
  726.      */
  727.     protected EventListenerList listenerList = new EventListenerList();
  728.  
  729.     /**
  730.      * Where the text is actually stored, and a set of marks
  731.      * that track change as the document is edited are managed.
  732.      */
  733.     private Content data;
  734.  
  735.     /**
  736.      * Factory for the attributes.  This is the strategy for
  737.      * attribute compression and control of the lifetime of
  738.      * a set of attributes as a collection.  This may be shared
  739.      * with other documents.
  740.      */
  741.     private AttributeContext context;
  742.  
  743.     private static final String BAD_LOCK_STATE = "document lock failure";
  744.  
  745.     /**
  746.      * Error message to indicate a bad location.
  747.      */
  748.     protected static final String BAD_LOCATION = "document location failure";
  749.  
  750.     /**
  751.      * Name of elements used to represent paragraphs
  752.      */
  753.     public static final String ParagraphElementName = "paragraph";
  754.  
  755.     /**
  756.      * Name of elements used to represent content
  757.      */
  758.     public static final String ContentElementName = "content";
  759.  
  760.     /**
  761.      * Name of elements used to hold sections (lines/paragraphs).
  762.      */
  763.     public static final String SectionElementName = "section";
  764.  
  765.     /**
  766.      * Name of the attribute used to specify element
  767.      * names.
  768.      */
  769.     public static final String ElementNameAttribute = "$ename";
  770.  
  771.     /**
  772.      * Interface to describe a sequence of character content that
  773.      * can be edited.  Implementations may or may not support a 
  774.      * history mechanism which will be reflected by whether or not
  775.      * mutations return an UndoableEdit implementation.  
  776.      * @see AbstractDocument
  777.      */
  778.     public interface Content {
  779.  
  780.     /**
  781.      * Creates a position within the content that will
  782.      * track change as the content is mutated.
  783.          *
  784.          * @param offset the offset in the content >= 0
  785.          * @return a Position
  786.          * @exception BadLocationException for an invalid offset
  787.      */
  788.     public Position createPosition(int offset) throws BadLocationException;
  789.  
  790.     /**
  791.      * Current length of the sequence of character content.
  792.          *
  793.          * @return the length >= 0
  794.      */
  795.         public int length();
  796.  
  797.     /**
  798.      * Inserts a string of characters into the sequence.
  799.      * 
  800.      * @param where   Offset into the sequence to make the insertion >= 0.
  801.      * @param str     String to insert.
  802.      * @return  If the implementation supports a history mechansim, 
  803.      *    a reference to an Edit implementation will be returned, 
  804.      *    otherwise null.
  805.      * @exception BadLocationException  Thrown if the area covered by
  806.      *   the arguments is not contained in the character sequence.
  807.      */
  808.         public UndoableEdit insertString(int where, String str) throws BadLocationException;
  809.  
  810.     /**
  811.      * Removes some portion of the sequence.  
  812.      *
  813.      * @param where   The offset into the sequence to make the
  814.          *   insertion >= 0.
  815.      * @param nitems  The number of items in the sequence to remove >= 0.
  816.      * @return  If the implementation supports a history mechansim, 
  817.      *    a reference to an Edit implementation will be returned, 
  818.      *    otherwise null.
  819.      * @exception BadLocationException  Thrown if the area covered by
  820.      *   the arguments is not contained in the character sequence.
  821.      */
  822.         public UndoableEdit remove(int where, int nitems) throws BadLocationException;
  823.  
  824.     /**
  825.      * Fetches a string of characters contained in the sequence.
  826.      * 
  827.      * @param where   Offset into the sequence to fetch >= 0.
  828.      * @param len     number of characters to copy >= 0.
  829.          * @return the string
  830.      * @exception BadLocationException  Thrown if the area covered by
  831.      *   the arguments is not contained in the character sequence.
  832.      */
  833.         public String getString(int where, int len) throws BadLocationException;
  834.  
  835.         /**
  836.          * Gets a sequence of characters and copies them into a Segment.
  837.          *
  838.          * @param where the starting offset >= 0
  839.          * @param len the number of characters >= 0
  840.          * @param txt the target location to copy into
  841.      * @exception BadLocationException  Thrown if the area covered by
  842.      *   the arguments is not contained in the character sequence.
  843.          */
  844.         public void getChars(int where, int len, Segment txt) throws BadLocationException;
  845.     }
  846.  
  847.     /**
  848.      * An interface that can be used to allow MutableAttributeSet 
  849.      * implementations to use pluggable attribute compression
  850.      * techniques.  Each mutation of the attribute set can be
  851.      * used to exchange a previous AttributeSet instance with
  852.      * another, preserving the possibility of the AttributeSet
  853.      * remaining immutable.  An implementation is provided by
  854.      * the StyleContext class.
  855.      *
  856.      * The Element implementations provided by this class use
  857.      * this interface to provide their MutableAttributeSet
  858.      * implementations, so that different AttributeSet compression
  859.      * techniques can be employed.  The method 
  860.      * <code>getAttributeContext</code> should be implemented to
  861.      * return the object responsible for implementing the desired
  862.      * compression technique.
  863.      * 
  864.      * @see StyleContext
  865.      */
  866.     public interface AttributeContext {
  867.  
  868.     /**
  869.      * Adds an attribute to the given set, and returns
  870.      * the new representative set.
  871.      *
  872.          * @param old the old attribute set
  873.      * @param name the non-null attribute name
  874.      * @param value the attribute value
  875.          * @return the updated attribute set
  876.      * @see MutableAttributeSet#addAttribute
  877.      */
  878.         public AttributeSet addAttribute(AttributeSet old, Object name, Object value);
  879.  
  880.     /**
  881.      * Adds a set of attributes to the element.
  882.      *
  883.          * @param old the old attribute set
  884.      * @param attr the attributes to add
  885.          * @return the updated attribute set
  886.      * @see MutableAttributeSet#addAttribute
  887.      */
  888.         public AttributeSet addAttributes(AttributeSet old, AttributeSet attr);
  889.  
  890.     /**
  891.      * Removes an attribute from the set.
  892.      *
  893.          * @param old the old attribute set
  894.      * @param name the non-null attribute name
  895.          * @return the updated attribute set
  896.      * @see MutableAttributeSet#removeAttribute
  897.      */
  898.         public AttributeSet removeAttribute(AttributeSet old, Object name);
  899.  
  900.     /**
  901.      * Removes a set of attributes for the element.
  902.      *
  903.          * @param old the old attribute set
  904.      * @param names the attribute names
  905.          * @return the updated attribute set
  906.      * @see MutableAttributeSet#removeAttributes
  907.      */
  908.         public AttributeSet removeAttributes(AttributeSet old, Enumeration names);
  909.  
  910.     /**
  911.      * Removes a set of attributes for the element.
  912.      *
  913.          * @param old the old attribute set
  914.      * @param attrs the attributes
  915.          * @return the updated attribute set
  916.      * @see MutableAttributeSet#removeAttributes
  917.      */
  918.         public AttributeSet removeAttributes(AttributeSet old, AttributeSet attrs);
  919.  
  920.     /**
  921.      * Fetches an empty AttributeSet.
  922.          *
  923.          * @return the attribute set
  924.      */
  925.     public AttributeSet getEmptySet();
  926.  
  927.     /**
  928.          * Reclaims an attribute set.
  929.      * This is a way for a MutableAttributeSet to mark that it no 
  930.      * longer need a particular immutable set.  This is only necessary
  931.      * in 1.1 where there are no weak references.  A 1.1 implementation
  932.      * would call this in its finalize method.
  933.          *
  934.          * @param a the attribute set to reclaim
  935.      */
  936.     public void reclaim(AttributeSet a);
  937.     }
  938.  
  939.     /**
  940.      * Implements the abstract part of an element.  By default elements
  941.      * support attributes by having a field that represents the immutable
  942.      * part of the current attribute set for the element.  The element itself
  943.      * implements MutableAttributeSet which can be used to modify the set
  944.      * by fetching a new immutable set.  The immutable sets are provided
  945.      * by the AttributeContext associated with the document.
  946.      * <p>
  947.      * Warning: serialized objects of this class will not be compatible with
  948.      * future swing releases.  The current serialization support is appropriate
  949.      * for short term storage or RMI between Swing1.0 applications.  It will
  950.      * not be possible to load serialized Swing1.0 objects with future releases
  951.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  952.      * baseline for the serialized form of Swing objects.
  953.      */
  954.     public abstract class AbstractElement implements Element, MutableAttributeSet, Serializable {
  955.  
  956.         /**
  957.          * Creates a new AbstractElement.
  958.          *
  959.          * @param parent the parent element
  960.          * @param a the attributes for the element
  961.          */
  962.         public AbstractElement(Element parent, AttributeSet a) {
  963.         attributes = (a != null) ? a.copyAttributes() : 
  964.         getAttributeContext().getEmptySet();
  965.         this.parent = parent;
  966.     }
  967.  
  968.     private final void indent(PrintStream out, int n) {
  969.         for (int i = 0; i < n; i++) {
  970.         out.print("  ");
  971.         }
  972.     }
  973.  
  974.     /**
  975.      * Dumps a debugging representation of the element hierarchy.
  976.          *
  977.          * @param out the output stream
  978.          * @param indentAmount the indentation level >= 0
  979.      */
  980.     public void dump(PrintStream out, int indentAmount) {
  981.         indent(out, indentAmount);
  982.         if (getName() == null) {
  983.         out.print("<??");
  984.         } else {
  985.         out.print("<" + getName());
  986.         }
  987.         if (getAttributeCount() > 0) {
  988.         out.println("");
  989.         // dump the attributes
  990.         Enumeration names = attributes.getAttributeNames();
  991.         while (names.hasMoreElements()) {
  992.             Object name = names.nextElement();
  993.             indent(out, indentAmount + 1);
  994.             out.println(name + "=" + getAttribute(name));
  995.         }
  996.         indent(out, indentAmount);
  997.         }
  998.         out.println(">");
  999.  
  1000.         if (isLeaf()) {
  1001.         indent(out, indentAmount+1);
  1002.         out.print("[" + getStartOffset() + "," + getEndOffset() + "]");
  1003.         Content c = getContent();
  1004.         try {
  1005.             String contentStr = c.getString(getStartOffset(), 
  1006.             getEndOffset() - getStartOffset()).trim();
  1007.             if (contentStr.length() > 40) {
  1008.             contentStr = contentStr.substring(0, 40) + "...";
  1009.             }
  1010.             out.println(contentStr);        
  1011.             } catch (BadLocationException e) {
  1012.             ;
  1013.         }
  1014.  
  1015.         } else {
  1016.         int n = getElementCount();
  1017.         for (int i = 0; i < n; i++) {
  1018.             AbstractElement e = (AbstractElement) getElement(i);
  1019.             e.dump(out, indentAmount+1);
  1020.         }
  1021.         }
  1022.     }
  1023.      
  1024.     // --- Object methods ---------------------------
  1025.  
  1026.         /**
  1027.          * Finalizes an AbstractElement.
  1028.          */
  1029.     protected void finalize() throws Throwable {
  1030.         AttributeContext context = getAttributeContext();
  1031.         context.reclaim(attributes);
  1032.     }
  1033.  
  1034.     // --- AttributeSet ----------------------------
  1035.     // delegated to the immutable field "attributes"
  1036.  
  1037.     /**
  1038.          * Gets the number of attributes that are defined.
  1039.          *
  1040.          * @return the number of attributes >= 0
  1041.      * @see AttributeSet#getAttributeCount
  1042.      */
  1043.         public int getAttributeCount() {
  1044.         return attributes.getAttributeCount();
  1045.     }
  1046.  
  1047.     /**
  1048.          * Checks whether a given attribute is defined.
  1049.          *
  1050.          * @param attrName the non-null attribute name
  1051.          * @return true if the attribute is defined
  1052.      * @see AttributeSet#isDefined
  1053.      */
  1054.         public boolean isDefined(Object attrName) {
  1055.         return attributes.isDefined(attrName);
  1056.     }
  1057.  
  1058.     /**
  1059.          * Checks whether two attribute sets are equal.
  1060.          *
  1061.          * @param attr the attribute set to check against
  1062.          * @return true if the same
  1063.      * @see AttributeSet#isEqual
  1064.      */
  1065.         public boolean isEqual(AttributeSet attr) {
  1066.         return attributes.isEqual(attr);
  1067.     }
  1068.  
  1069.     /**
  1070.          * Copies a set of attributes.
  1071.          *
  1072.          * @return the copy
  1073.      * @see AttributeSet#copyAttributes
  1074.      */
  1075.         public AttributeSet copyAttributes() {
  1076.         return attributes;
  1077.     }
  1078.  
  1079.     /**
  1080.          * Gets the value of an attribute.
  1081.          *
  1082.          * @param attrName the non-null attribute name
  1083.          * @return the attribute value
  1084.      * @see AttributeSet#getAttribute
  1085.      */
  1086.         public Object getAttribute(Object attrName) {
  1087.         Object value = attributes.getAttribute(attrName);
  1088.         if (value == null) {
  1089.         // The delegate nor it's resolvers had a match,
  1090.         // so we'll try to resolve through the parent
  1091.         // element.
  1092.         AttributeSet a = (parent != null) ? parent.getAttributes() : null;
  1093.         if (a != null) {
  1094.             value = a.getAttribute(attrName);
  1095.         }
  1096.         }
  1097.         return value;
  1098.     }
  1099.  
  1100.     /**
  1101.          * Gets the names of all attributes.
  1102.          *
  1103.          * @return the attribute names as an enumeration
  1104.      * @see AttributeSet#getAttributeNames
  1105.      */
  1106.         public Enumeration getAttributeNames() {
  1107.         return attributes.getAttributeNames();
  1108.     }
  1109.  
  1110.     /**
  1111.          * Checks whether a given attribute name/value is defined.
  1112.          *
  1113.          * @param name the non-null attribute name
  1114.          * @param value the attribute value
  1115.          * @return true if the name/value is defined
  1116.      * @see AttributeSet#containsAttribute
  1117.      */
  1118.         public boolean containsAttribute(Object name, Object value) {
  1119.         return attributes.containsAttribute(name, value);
  1120.     }
  1121.  
  1122.  
  1123.     /**
  1124.          * Checks whether the element contains all the attributes.
  1125.          *
  1126.          * @param attrs the attributes to check
  1127.          * @return true if the element contains all the attributes
  1128.      * @see AttributeSet#containsAttributes
  1129.      */
  1130.         public boolean containsAttributes(AttributeSet attrs) {
  1131.         return attributes.containsAttributes(attrs);
  1132.     }
  1133.  
  1134.     /**
  1135.          * Gets the resolving parent.
  1136.      * If not overriden, the resolving parent defaults to 
  1137.      * the parent element.
  1138.          *
  1139.          * @return the attributes from the parent, null if none
  1140.      * @see AttributeSet#getResolveParent
  1141.      */
  1142.         public AttributeSet getResolveParent() {
  1143.         AttributeSet a = attributes.getResolveParent();
  1144.         if ((a == null) && (parent != null)) {
  1145.         a = parent.getAttributes();
  1146.         }
  1147.         return a;
  1148.     }
  1149.  
  1150.     // --- MutableAttributeSet ----------------------------------
  1151.     // should fetch a new immutable record for the field
  1152.     // "attributes".
  1153.  
  1154.     /**
  1155.          * Adds an attribute to the element.
  1156.          *
  1157.          * @param name the non-null attribute name
  1158.          * @param value the attribute value
  1159.      * @see MutableAttributeSet#addAttribute
  1160.      */
  1161.         public void addAttribute(Object name, Object value) {
  1162.         checkForIllegalCast();
  1163.         AttributeContext context = getAttributeContext();
  1164.         attributes = context.addAttribute(attributes, name, value);
  1165.     }
  1166.  
  1167.     /**
  1168.          * Adds a set of attributes to the element.
  1169.          *
  1170.          * @param attr the attributes to add
  1171.      * @see MutableAttributeSet#addAttribute
  1172.      */
  1173.         public void addAttributes(AttributeSet attr) {
  1174.         checkForIllegalCast();
  1175.         AttributeContext context = getAttributeContext();
  1176.         attributes = context.addAttributes(attributes, attr);
  1177.     }
  1178.  
  1179.     /**
  1180.          * Removes an attribute from the set.
  1181.          *
  1182.          * @param name the non-null attribute name
  1183.      * @see MutableAttributeSet#removeAttribute
  1184.      */
  1185.         public void removeAttribute(Object name) {
  1186.         checkForIllegalCast();
  1187.         AttributeContext context = getAttributeContext();
  1188.         attributes = context.removeAttribute(attributes, name);
  1189.     }
  1190.  
  1191.     /**
  1192.          * Removes a set of attributes for the element.
  1193.          *
  1194.          * @param names the attribute names
  1195.      * @see MutableAttributeSet#removeAttributes
  1196.      */
  1197.         public void removeAttributes(Enumeration names) {
  1198.         checkForIllegalCast();
  1199.         AttributeContext context = getAttributeContext();
  1200.         attributes = context.removeAttributes(attributes, names);
  1201.     }
  1202.  
  1203.     /**
  1204.          * Removes a set of attributes for the element.
  1205.          *
  1206.          * @param attrs the attributes
  1207.      * @see MutableAttributeSet#removeAttributes
  1208.      */
  1209.         public void removeAttributes(AttributeSet attrs) {
  1210.         checkForIllegalCast();
  1211.         AttributeContext context = getAttributeContext();
  1212.         if (attrs == this) {
  1213.         attributes = context.getEmptySet();
  1214.         } else {
  1215.         attributes = context.removeAttributes(attributes, attrs);
  1216.         }
  1217.     }
  1218.  
  1219.     /**
  1220.          * Sets the resolving parent.
  1221.          *
  1222.          * @param parent the parent, null if none
  1223.      * @see MutableAttributeSet#setResolveParent
  1224.      */
  1225.         public void setResolveParent(AttributeSet parent) {
  1226.         checkForIllegalCast();
  1227.         AttributeContext context = getAttributeContext();
  1228.         if (parent != null) {
  1229.         attributes = 
  1230.             context.addAttribute(attributes, StyleConstants.ResolveAttribute,
  1231.                      parent);
  1232.         } else {
  1233.         attributes = 
  1234.             context.removeAttribute(attributes, StyleConstants.ResolveAttribute);
  1235.         }
  1236.     }
  1237.  
  1238.     private final void checkForIllegalCast() {
  1239.         Thread t = getCurrentWriter();
  1240.         if ((t == null) || (t != Thread.currentThread())) {
  1241.         throw new StateInvariantError("Illegal cast to MutableAttributeSet");
  1242.         }
  1243.     }
  1244.  
  1245.         // --- Element methods -------------------------------------
  1246.  
  1247.         /**
  1248.          * Retrieves the underlying model.
  1249.          *
  1250.          * @return the model
  1251.          */
  1252.     public Document getDocument() {
  1253.         return AbstractDocument.this;
  1254.     }
  1255.  
  1256.         /**
  1257.          * Gets the parent of the element.
  1258.          *
  1259.          * @return the parent
  1260.          */
  1261.     public Element getParentElement() {
  1262.         return parent;
  1263.     }
  1264.  
  1265.         /**
  1266.          * Gets the attributes for the element.
  1267.          *
  1268.          * @return the attribute set
  1269.          */
  1270.     public AttributeSet getAttributes() {
  1271.         return this;
  1272.     }
  1273.  
  1274.         /**
  1275.          * Gets the name of the element.
  1276.          *
  1277.          * @return the name, null if none
  1278.          */
  1279.         public String getName() {
  1280.         if (attributes.isDefined(ElementNameAttribute)) {
  1281.         return (String) attributes.getAttribute(ElementNameAttribute);
  1282.         }
  1283.         return null;
  1284.     }
  1285.  
  1286.         /**
  1287.          * Gets the starting offset in the model for the element.
  1288.          *
  1289.          * @return the offset >= 0
  1290.          */
  1291.     public abstract int getStartOffset();
  1292.  
  1293.         /**
  1294.          * Gets the ending offset in the model for the element.
  1295.          *
  1296.          * @return the offset >= 0
  1297.          */
  1298.     public abstract int getEndOffset();
  1299.     
  1300.         /**
  1301.          * Gets a child element.
  1302.          *
  1303.          * @param index the child index, >= 0 && < getElementCount()
  1304.          * @return the child element
  1305.          */
  1306.     public abstract Element getElement(int index);
  1307.  
  1308.         /**
  1309.          * Gets the number of children for the element.
  1310.          *
  1311.          * @return the number of children >= 0
  1312.          */
  1313.     public abstract int getElementCount();
  1314.  
  1315.         /**
  1316.          * Gets the child element index closest to the given model offset.
  1317.          *
  1318.          * @param offset the offset >= 0
  1319.          * @return the element index >= 0
  1320.          */
  1321.     public abstract int getElementIndex(int offset);
  1322.  
  1323.         /**
  1324.          * Checks whether the element is a leaf.
  1325.          *
  1326.          * @return true if a leaf
  1327.          */
  1328.     public abstract boolean isLeaf();
  1329.  
  1330.     // --- serialization ---------------------------------------------
  1331.  
  1332.         private void writeObject(ObjectOutputStream s) throws IOException {
  1333.         s.defaultWriteObject();
  1334.         StyleContext.writeAttributeSet(s, attributes);
  1335.     }
  1336.  
  1337.         private void readObject(ObjectInputStream s)
  1338.             throws ClassNotFoundException, IOException 
  1339.         {
  1340.         s.defaultReadObject();
  1341.         MutableAttributeSet attr = new SimpleAttributeSet();
  1342.         StyleContext.readAttributeSet(s, attr);
  1343.         AttributeContext context = getAttributeContext();
  1344.         attributes = context.addAttributes(SimpleAttributeSet.EMPTY, attr);
  1345.     }
  1346.  
  1347.     // ---- variables -----------------------------------------------------
  1348.  
  1349.     private Element parent;
  1350.     private transient AttributeSet attributes;
  1351.  
  1352.     }
  1353.  
  1354.     /**
  1355.      * Implements a composite element that contains other elements.
  1356.      * <p>
  1357.      * Warning: serialized objects of this class will not be compatible with
  1358.      * future swing releases.  The current serialization support is appropriate
  1359.      * for short term storage or RMI between Swing1.0 applications.  It will
  1360.      * not be possible to load serialized Swing1.0 objects with future releases
  1361.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  1362.      * baseline for the serialized form of Swing objects.
  1363.      */
  1364.     public class BranchElement extends AbstractElement {
  1365.  
  1366.     /**
  1367.      * Constructs a composite element that initially contains
  1368.      * no children.
  1369.      *
  1370.      * @param parent  The parent element
  1371.          * @param a the attributes for the element
  1372.      */
  1373.     public BranchElement(Element parent, AttributeSet a) {
  1374.         super(parent, a);
  1375.         children = new AbstractElement[1];
  1376.         nchildren = 0;
  1377.         lastIndex = -1;
  1378.     }
  1379.  
  1380.     /**
  1381.      * Gets the child element that contains
  1382.      * the given model position.
  1383.          *
  1384.          * @param pos the position >= 0
  1385.          * @return the element, null if none
  1386.      */
  1387.     public Element positionToElement(int pos) {
  1388.         int index = getElementIndex(pos);
  1389.         Element child = children[index];
  1390.         int p0 = child.getStartOffset();
  1391.         int p1 = child.getEndOffset();
  1392.         if ((pos >= p0) && (pos < p1)) {
  1393.         return child;
  1394.         }
  1395.         return null;
  1396.     }
  1397.  
  1398.         /**
  1399.          * Replaces content with a new set of elements.
  1400.          *
  1401.          * @param offset the starting offset >= 0
  1402.          * @param length the length to replace >= 0
  1403.          * @param elems the new elements
  1404.          */
  1405.         public void replace(int offset, int length, Element[] elems) {
  1406.         int delta = elems.length - length;
  1407.         int src = offset + length;
  1408.         int nmove = nchildren - src;
  1409.         int dest = src + delta;
  1410.         if ((nchildren + delta) >= children.length) {
  1411.         // need to grow the array
  1412.         int newLength = Math.max(2*children.length, nchildren + delta);
  1413.         AbstractElement[] newChildren = new AbstractElement[newLength];
  1414.         System.arraycopy(children, 0, newChildren, 0, offset);
  1415.         System.arraycopy(elems, 0, newChildren, offset, elems.length);
  1416.         System.arraycopy(children, src, newChildren, dest, nmove);
  1417.         children = newChildren;
  1418.         } else {
  1419.         // patch the existing array
  1420.         System.arraycopy(children, src, children, dest, nmove);
  1421.         System.arraycopy(elems, 0, children, offset, elems.length);
  1422.         }
  1423.         nchildren = nchildren + delta;
  1424.     }
  1425.  
  1426.         /**
  1427.          * Converts the element to a string.
  1428.          *
  1429.          * @return the string
  1430.          */
  1431.     public String toString() {
  1432.         return "BranchElement(" + getName() + ") " + getStartOffset() + "," +
  1433.         getEndOffset() + "\n";
  1434.     }
  1435.  
  1436.     // --- Element methods -----------------------------------
  1437.  
  1438.         /**
  1439.          * Gets the element name.
  1440.          *
  1441.          * @return the element name
  1442.          */
  1443.     public String getName() {
  1444.         String nm = super.getName();
  1445.         if (nm == null) {
  1446.         nm = ParagraphElementName;
  1447.         }
  1448.         return nm;
  1449.     }
  1450.  
  1451.         /**
  1452.          * Gets the starting offset in the model for the element.
  1453.          *
  1454.          * @return the offset >= 0
  1455.          */
  1456.         public int getStartOffset() {
  1457.         return children[0].getStartOffset();
  1458.     }
  1459.  
  1460.         /**
  1461.          * Gets the ending offset in the model for the element.
  1462.          *
  1463.          * @return the offset >= 0
  1464.          */
  1465.         public int getEndOffset() {
  1466.         Element child = children[nchildren - 1];
  1467.         return child.getEndOffset();
  1468.     }
  1469.     
  1470.         /**
  1471.          * Gets a child element.
  1472.          *
  1473.          * @param index the child index, >= 0 && < getElementCount()
  1474.          * @return the child element, null if none
  1475.          */
  1476.     public Element getElement(int index) {
  1477.         if (index < nchildren) {
  1478.         return children[index];
  1479.         }
  1480.         return null;
  1481.     }
  1482.  
  1483.         /**
  1484.          * Gets the number of children for the element.
  1485.          *
  1486.          * @return the number of children >= 0
  1487.          */
  1488.     public int getElementCount()  {
  1489.         return nchildren;
  1490.     }
  1491.  
  1492.         /**
  1493.          * Gets the child element index closest to the given model offset.
  1494.          *
  1495.          * @param offset the offset >= 0
  1496.          * @return the element index >= 0
  1497.          */
  1498.     public int getElementIndex(int offset) {
  1499.         int index;
  1500.         int lower = 0; 
  1501.         int upper = nchildren - 1;
  1502.         int mid = 0;
  1503.         int p0 = getStartOffset();
  1504.         int p1;
  1505.  
  1506.         if (nchildren == 0) {
  1507.         return 0;
  1508.         }
  1509.         if (offset >= getEndOffset()) {
  1510.         return nchildren - 1;
  1511.         }
  1512.  
  1513.         // see if the last index can be used.
  1514.         if ((lastIndex >= lower) && (lastIndex <= upper)) {
  1515.         Element lastHit = children[lastIndex];
  1516.         p0 = lastHit.getStartOffset();
  1517.         p1 = lastHit.getEndOffset();
  1518.         if ((offset >= p0) && (offset < p1)) {
  1519.             return lastIndex;
  1520.         }
  1521.  
  1522.         // last index wasn't a hit, but it does give useful info about
  1523.         // where a hit (if any) would be.
  1524.         if (offset < p0) {
  1525.             upper = lastIndex;
  1526.         } else  {
  1527.             lower = lastIndex;
  1528.         }  
  1529.         }
  1530.  
  1531.         while (lower <= upper) {
  1532.         mid = lower + ((upper - lower) / 2);
  1533.         Element elem = children[mid];
  1534.         p0 = elem.getStartOffset();
  1535.         p1 = elem.getEndOffset();
  1536.         if ((offset >= p0) && (offset < p1)) {
  1537.             // found the location
  1538.             index = mid;
  1539.             lastIndex = index;
  1540.             return index;
  1541.         } else if (offset < p0) {        
  1542.             upper = mid - 1;
  1543.         } else {
  1544.             lower = mid + 1;
  1545.         }
  1546.         }
  1547.  
  1548.         // didn't find it, but we indicate the index of where it would belong
  1549.         if (offset < p0) {
  1550.         index = mid;
  1551.         } else {
  1552.         index = mid + 1;
  1553.         }
  1554.         lastIndex = index;
  1555.         return index;   
  1556.     }
  1557.  
  1558.         /**
  1559.          * Checks whether the element is a leaf.
  1560.          *
  1561.          * @return true if a leaf
  1562.          */
  1563.     public boolean isLeaf() {
  1564.         return false;
  1565.     }
  1566.  
  1567.     // ------ members ----------------------------------------------
  1568.  
  1569.     private AbstractElement[] children;
  1570.     private int nchildren;
  1571.     private int lastIndex;
  1572.     }
  1573.     
  1574.     /**
  1575.      * Implements an element that directly represents content of
  1576.      * some kind.
  1577.      * <p>
  1578.      * Warning: serialized objects of this class will not be compatible with
  1579.      * future swing releases.  The current serialization support is appropriate
  1580.      * for short term storage or RMI between Swing1.0 applications.  It will
  1581.      * not be possible to load serialized Swing1.0 objects with future releases
  1582.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  1583.      * baseline for the serialized form of Swing objects.
  1584.      *
  1585.      * @see     Element
  1586.      */
  1587.     public class LeafElement extends AbstractElement {
  1588.  
  1589.     /**
  1590.      * Constructs an element that represents content within the
  1591.      * document (has no children).
  1592.      *
  1593.      * @param parent  The parent element
  1594.      * @param a       The element attributes
  1595.      * @param offs0   The start offset >= 0
  1596.      * @param offs1   The end offset >= offs0
  1597.      */
  1598.     public LeafElement(Element parent, AttributeSet a, int offs0, int offs1) {
  1599.         super(parent, a);
  1600.         try {
  1601.         p0 = createPosition(offs0);
  1602.         p1 = createPosition(offs1);
  1603.         } catch (BadLocationException e) {
  1604.         p0 = null;
  1605.         p1 = null;
  1606.         throw new StateInvariantError("Can't create Position references");
  1607.         }
  1608.     }
  1609.  
  1610.         /**
  1611.          * Converts the element to a string.
  1612.          *
  1613.          * @return the string
  1614.          */
  1615.     public String toString() {
  1616.         return "LeafElement(" + getName() + ") " + p0 + "," + p1 + "\n";
  1617.     }
  1618.  
  1619.     // --- Element methods ---------------------------------------------
  1620.  
  1621.         /**
  1622.          * Gets the starting offset in the model for the element.
  1623.          *
  1624.          * @return the offset >= 0
  1625.          */
  1626.     public int getStartOffset() {
  1627.         return p0.getOffset();
  1628.     }
  1629.  
  1630.         /**
  1631.          * Gets the ending offset in the model for the element.
  1632.          *
  1633.          * @return the offset >= 0
  1634.          */
  1635.     public int getEndOffset() {
  1636.         return p1.getOffset();
  1637.     }
  1638.  
  1639.         /**
  1640.          * Gets the element name.
  1641.          *
  1642.          * @return the name
  1643.          */
  1644.     public String getName() {
  1645.         String nm = super.getName();
  1646.         if (nm == null) {
  1647.         nm = ContentElementName;
  1648.         }
  1649.         return nm;
  1650.     }
  1651.  
  1652.         /**
  1653.          * Gets the child element index closest to the given model offset.
  1654.          *
  1655.          * @param pos the offset >= 0
  1656.          * @return the element index >= 0
  1657.          */
  1658.     public int getElementIndex(int pos) {
  1659.         return -1;
  1660.     }
  1661.  
  1662.         /**
  1663.          * Gets a child element.
  1664.          *
  1665.          * @param index the child index, >= 0 && < getElementCount()
  1666.          * @return the child element
  1667.          */
  1668.     public Element getElement(int index) {
  1669.         return null;
  1670.     }
  1671.  
  1672.         /**
  1673.          * Returns the number of child elements.
  1674.          *
  1675.          * @return the number of children >= 0
  1676.          */
  1677.     public int getElementCount()  {
  1678.         return 0;
  1679.     }
  1680.  
  1681.         /**
  1682.          * Checks whether the element is a leaf.
  1683.          *
  1684.          * @return true if a leaf
  1685.          */
  1686.     public boolean isLeaf() {
  1687.         return true;
  1688.     }
  1689.  
  1690.     // --- serialization ---------------------------------------------
  1691.  
  1692.         private void writeObject(ObjectOutputStream s) throws IOException {
  1693.         s.defaultWriteObject();
  1694.         s.writeInt(p0.getOffset());
  1695.         s.writeInt(p1.getOffset());
  1696.     }
  1697.  
  1698.         private void readObject(ObjectInputStream s)
  1699.             throws ClassNotFoundException, IOException 
  1700.         {
  1701.         s.defaultReadObject();
  1702.  
  1703.         // set the range with positions that track change
  1704.         int off0 = s.readInt();
  1705.         int off1 = s.readInt();
  1706.         try {
  1707.         p0 = createPosition(off0);
  1708.         p1 = createPosition(off1);
  1709.         } catch (BadLocationException e) {
  1710.         p0 = null;
  1711.         p1 = null;
  1712.         throw new IOException("Can't restore Position references");
  1713.         }
  1714.     }
  1715.  
  1716.     // ---- members -----------------------------------------------------
  1717.  
  1718.     private transient Position p0;
  1719.     private transient Position p1;
  1720.     }
  1721.  
  1722.     /**
  1723.      * Stores document changes as the document is being
  1724.      * modified.  Can subsequently be used for change notification
  1725.      * when done with the document modification transaction.
  1726.      * This is used by the AbstractDocument class and its extensions
  1727.      * for broadcasting change information to the document listeners.
  1728.      */
  1729.     public class DefaultDocumentEvent extends CompoundEdit implements DocumentEvent {
  1730.  
  1731.     /**
  1732.      * Constructs a change record.
  1733.      *
  1734.      * @param offs the offset into the document of the change >= 0
  1735.      * @param len  the length of the change >= 0
  1736.      * @param type the type of event (DocumentEvent.EventType)
  1737.      */
  1738.         public DefaultDocumentEvent(int offs, int len, DocumentEvent.EventType type) {
  1739.         super();
  1740.         offset = offs;
  1741.         length = len;
  1742.         this.type = type;
  1743.     }
  1744.  
  1745.     /**
  1746.      * Returns a string description of the change event.
  1747.      *
  1748.      * @return a string
  1749.      */
  1750.         public String toString() {
  1751.         return edits.toString();
  1752.     }
  1753.     
  1754.     // --- CompoundEdit methods --------------------------
  1755.  
  1756.     /**
  1757.      * Adds a document edit.  If the number of edits crosses
  1758.      * a threshold, this switches on a hashtable lookup for
  1759.      * ElementChange implementations since access of these
  1760.      * needs to be relatively quick.
  1761.      *
  1762.      * @param anEdit a document edit record
  1763.      * @return true if the edit was added
  1764.      */ 
  1765.         public boolean addEdit(UndoableEdit anEdit) {
  1766.         // if the number of changes gets too great, start using
  1767.         // a hashtable for to locate the change for a given element.
  1768.         if ((changeLookup == null) && (edits.size() > 10)) {
  1769.         changeLookup = new Hashtable();
  1770.         int n = edits.size();
  1771.         for (int i = 0; i < n; i++) {
  1772.             Object o = edits.elementAt(i);
  1773.             if (o instanceof DocumentEvent.ElementChange) {
  1774.             DocumentEvent.ElementChange ec = (DocumentEvent.ElementChange) o;
  1775.             changeLookup.put(ec.getElement(), ec);
  1776.             }
  1777.         }
  1778.         }
  1779.  
  1780.         // if we have a hashtable... add the entry if it's 
  1781.         // an ElementChange.
  1782.         if ((changeLookup != null) && (anEdit instanceof DocumentEvent.ElementChange)) {
  1783.         DocumentEvent.ElementChange ec = (DocumentEvent.ElementChange) anEdit;
  1784.         changeLookup.put(ec.getElement(), ec);
  1785.         }
  1786.         return super.addEdit(anEdit);
  1787.     }
  1788.  
  1789.     /**
  1790.      * Redoes a change.
  1791.      *
  1792.      * @exception CannotRedoException if the change cannot be redone
  1793.      */
  1794.         public void redo() throws CannotRedoException {
  1795.         writeLock();
  1796.         try {
  1797.         // change the state
  1798.         super.redo();
  1799.         // fire a DocumentEvent to notify the view(s)
  1800.         if (type == DocumentEvent.EventType.INSERT) {
  1801.             fireInsertUpdate(this);
  1802.         } else if (type == DocumentEvent.EventType.REMOVE) {
  1803.             fireRemoveUpdate(this);
  1804.         } else {
  1805.             fireChangedUpdate(this);
  1806.         }
  1807.         } finally {
  1808.         writeUnlock();
  1809.         }
  1810.     }
  1811.  
  1812.     /**
  1813.      * Undoes a change.
  1814.      *
  1815.      * @exception CannotUndoException if the change cannot be undone
  1816.      */
  1817.         public void undo() throws CannotUndoException {
  1818.         writeLock();
  1819.         try {
  1820.         // change the state
  1821.         super.undo();
  1822.         // fire a DocumentEvent to notify the view(s)
  1823.         if (type == DocumentEvent.EventType.REMOVE) {
  1824.             fireInsertUpdate(this);
  1825.         } else if (type == DocumentEvent.EventType.INSERT) {
  1826.             fireRemoveUpdate(this);
  1827.         } else {
  1828.             fireChangedUpdate(this);
  1829.         }
  1830.         } finally {
  1831.         writeUnlock();
  1832.         }
  1833.     }
  1834.  
  1835.     /**
  1836.      * DefaultDocument events are significant.  If you wish to aggregate
  1837.      * DefaultDocumentEvents to present them as a single edit to the user
  1838.      * place them into a CompoundEdit.
  1839.          *
  1840.          * @return whether the event is significant for edit undo purposes
  1841.      */
  1842.     public boolean isSignificant() {
  1843.         return true;
  1844.     }
  1845.  
  1846.  
  1847.     /**
  1848.      * Provides a localized, human readable description of this edit
  1849.      * suitable for use in, say, a change log.
  1850.          *
  1851.          * @return the description
  1852.      */
  1853.     public String getPresentationName() {
  1854.         DocumentEvent.EventType type = getType();
  1855.         if(type == DocumentEvent.EventType.INSERT)
  1856.         return "addition";
  1857.         if(type == DocumentEvent.EventType.REMOVE)
  1858.         return "deletion";
  1859.         return "style change";
  1860.     }
  1861.  
  1862.     /**
  1863.      * Provides a localized, human readable description of the undoable
  1864.      * form of this edit, e.g. for use as an Undo menu item. Typically
  1865.      * derived from getDescription();
  1866.          *
  1867.          * @return the description
  1868.      */
  1869.     public String getUndoPresentationName() {
  1870.         return "Undo " + getPresentationName();
  1871.     }
  1872.  
  1873.     /**
  1874.      * Provides a localized, human readable description of the redoable
  1875.      * form of this edit, e.g. for use as a Redo menu item. Typically
  1876.      * derived from getPresentationName();
  1877.          *
  1878.          * @return the description
  1879.      */
  1880.     public String getRedoPresentationName() {
  1881.         return "Redo " + getPresentationName();
  1882.     }
  1883.  
  1884.     // --- DocumentEvent methods --------------------------
  1885.  
  1886.     /**
  1887.      * Returns the type of event.
  1888.          *
  1889.          * @return the event type as a DocumentEvent.EventType
  1890.      * @see DocumentEvent#getType
  1891.      */
  1892.     public DocumentEvent.EventType getType() {
  1893.         return type;
  1894.     }
  1895.  
  1896.     /**
  1897.      * Returns the offset within the document of the start of the change.
  1898.      *
  1899.      * @return the offset >= 0
  1900.      * @see DocumentEvent#getOffset
  1901.      */
  1902.         public int getOffset() {
  1903.         return offset;
  1904.     }
  1905.  
  1906.     /**
  1907.      * Returns the length of the change.
  1908.      *
  1909.      * @return the length >= 0
  1910.      * @see DocumentEvent#getLength
  1911.      */
  1912.         public int getLength() {
  1913.         return length;
  1914.     }
  1915.     
  1916.     /**
  1917.      * Gets the document that sourced the change event.
  1918.      *
  1919.      * @return the document
  1920.      * @see DocumentEvent#getDocument
  1921.      */
  1922.         public Document getDocument() {
  1923.         return AbstractDocument.this;
  1924.     }
  1925.  
  1926.     /**
  1927.      * Gets the changes for an element.
  1928.      *
  1929.      * @param elem the element
  1930.      * @return the changes
  1931.      */
  1932.         public DocumentEvent.ElementChange getChange(Element elem) {
  1933.         if (changeLookup != null) {
  1934.         return (DocumentEvent.ElementChange) changeLookup.get(elem);
  1935.         }
  1936.         int n = edits.size();
  1937.         for (int i = 0; i < n; i++) {
  1938.         Object o = edits.elementAt(i);
  1939.         if (o instanceof DocumentEvent.ElementChange) {
  1940.             DocumentEvent.ElementChange c = (DocumentEvent.ElementChange) o;
  1941.             if (c.getElement() == elem) {
  1942.             return c;
  1943.             }
  1944.         }
  1945.         }
  1946.         return null;
  1947.     }
  1948.  
  1949.     // --- member variables ------------------------------------
  1950.  
  1951.         private int offset;
  1952.         private int length;
  1953.         private Hashtable changeLookup;
  1954.     private DocumentEvent.EventType type;
  1955.  
  1956.     }
  1957.  
  1958.     /**
  1959.      * An implementation of ElementChange that can be added to the document
  1960.      * event.
  1961.      */
  1962.     public static class ElementEdit extends AbstractUndoableEdit implements DocumentEvent.ElementChange {
  1963.  
  1964.     /**
  1965.      * Constructs an edit record.  This does not modify the element
  1966.      * so it can safely be used to <em>catch up</em> a view to the
  1967.      * current model state for views that just attached to a model.
  1968.      *
  1969.      * @param e the element
  1970.      * @param index the index into the model >= 0
  1971.      * @param removed a set of elements that were removed
  1972.      * @param added a set of elements that were added
  1973.      */
  1974.         public ElementEdit(Element e, int index, Element[] removed, Element[] added) {
  1975.         super();
  1976.         this.e = e;
  1977.         this.index = index;
  1978.         this.removed = removed;
  1979.         this.added = added;
  1980.     }
  1981.         
  1982.     /**
  1983.      * Returns the underlying element.
  1984.      *
  1985.      * @return the element
  1986.      */
  1987.         public Element getElement() {
  1988.         return e;
  1989.     } 
  1990.  
  1991.     /**
  1992.      * Returns the index into the list of elements.
  1993.      *
  1994.      * @return the index >= 0
  1995.      */
  1996.         public int getIndex() {
  1997.         return index;
  1998.     }
  1999.  
  2000.     /**
  2001.      * Gets a list of children that were removed.
  2002.      *
  2003.      * @return the list
  2004.      */
  2005.         public Element[] getChildrenRemoved() {
  2006.         return removed;
  2007.     }
  2008.  
  2009.     /**
  2010.      * Gets a list of children that were added.
  2011.      *
  2012.      * @return the list
  2013.      */
  2014.         public Element[] getChildrenAdded() {
  2015.         return added;
  2016.     }
  2017.  
  2018.     /**
  2019.      * Redoes a change.
  2020.      *
  2021.      * @exception CannotRedoException if the change cannot be redone
  2022.      */
  2023.         public void redo() throws CannotRedoException {
  2024.         super.redo();
  2025.  
  2026.         // Since this event will be reused, switch around added/removed.
  2027.         Element[] tmp = removed;
  2028.         removed = added;
  2029.         added = tmp;
  2030.  
  2031.         // PENDING(prinz) need MutableElement interface, canRedo() should check
  2032.         ((AbstractDocument.BranchElement)e).replace(index, removed.length, added);
  2033.     }
  2034.  
  2035.     /**
  2036.      * Undoes a change.
  2037.      *
  2038.      * @exception CannotUndoException if the change cannot be undone
  2039.      */
  2040.         public void undo() throws CannotUndoException {
  2041.         super.undo();
  2042.         // PENDING(prinz) need MutableElement interface, canUndo() should check
  2043.         ((AbstractDocument.BranchElement)e).replace(index, added.length, removed);
  2044.  
  2045.         // Since this event will be reused, switch around added/removed.
  2046.         Element[] tmp = removed;
  2047.         removed = added;
  2048.         added = tmp;
  2049.     }
  2050.  
  2051.         private Element e;
  2052.     private int index;
  2053.     private Element[] removed;
  2054.     private Element[] added;
  2055.     }
  2056.  
  2057. }
  2058.  
  2059.  
  2060.  
  2061.  
  2062.  
  2063.  
  2064.     
  2065.